import base64
import json
import os
import urllib

import requests
from PyQt5.QtCore import Qt, QIODevice
from PyQt5.QtGui import QIcon
from PyQt5.QtMultimedia import QAudio, QAudioDeviceInfo, QAudioFormat, QAudioInput
from PyQt5.QtWidgets import QApplication, QDialog, QHBoxLayout, QLabel, QPushButton, QVBoxLayout


API_KEY = "a6K6PGIYgsFgWCHC2pMD9uZH"
SECRET_KEY = "RwMyoPGniJsTnf2zj4DBQKajBCPeHvUA"


class RecordDialog(QDialog):
    def __init__(self, parent=None):
        super(RecordDialog, self).__init__(parent)
        layout = QVBoxLayout()
        self.btn_record = QPushButton("开始录音", self)
        self.btn_record.clicked.connect(self.start_recording)
        layout.addWidget(self.btn_record)

        self.label = QLabel("请点击按钮开始录音", self)
        layout.addWidget(self.label)

        self.setLayout(layout)
        self.setWindowTitle("语音识别")

        self.audio_input = None
        self.buffer = None
        self.save_path = ""

    def start_recording(self):
        if not self.audio_input:
            self.init_audio_input()
        if not self.audio_input:
            return

        if self.audio_input.state() == QAudio.StoppedState:
            self.audio_input.start(self.buffer)

            self.btn_record.setText("停止录音")
            self.label.setText("正在录制声音...")
        elif self.audio_input.state() == QAudio.ActiveState:
            self.audio_input.stop()

            file_path = os.path.join(os.path.dirname(__file__), "audio", "record.m4a")
            self.save_to_file(file_path)

            self.btn_record.setText("开始录音")
            self.label.setText("录制完毕，正在进行语音识别...")
            self.recognize_audio(file_path)

    def init_audio_input(self):
        device_info = QAudioDeviceInfo.defaultInputDevice()
        if not device_info.isFormatSupported(QAudioFormat()):
            self.label.setText("当前麦克风不受支持")
            return

        format_ = QAudioFormat()
        format_.setCodec("audio/pcm")
        format_.setSampleRate(16000)
        format_.setChannelCount(1)
        format_.setSampleSize(16)
        format_.setByteOrder(QAudioFormat.LittleEndian)
        format_.setSampleType(QAudioFormat.SignedInt)

        self.buffer = QAudioInput(format_)
        self.audio_input = QAudioInput(device_info, format_, self)

    def save_to_file(self, file_path):
        self.save_path = file_path
        buffer = self.buffer.readAll()
        with open(file_path, "wb") as f:
            f.write(buffer.data())

    def recognize_audio(self, file_path):
        token = get_access_token()
        speech = get_file_content_as_base64(file_path)

        url = "https://vop.baidu.com/server_api"
        payload = json.dumps({
            "format": "m4a",
            "rate": 16000,
            "channel": 1,
            "cuid": "fUZte2O4Rrvmf2CttkrYeO34ZXttW4lt",
            "token": token,
            "speech": speech,
            "len": os.path.getsize(file_path),
        })
        headers = {
            'Content-Type': 'application/json',
            'Accept': 'application/json'
        }

        response = requests.post(url, headers=headers, data=payload)
        if response.status_code == 200:
            result = json.loads(response.text)
            if result.get("err_no") == 0:
                text = result.get("result")[0]
                self.label.setText(text)
            else:
                self.label.setText("识别失败，请重试")
        else:
            self.label.setText("网络错误，请检查网络连接")


def main():
    app = QApplication([])
    app.setWindowIcon(QIcon(os.path.join(os.path.dirname(__file__), "icon.png")))
    dialog = RecordDialog()
    dialog.show()
    app.exec()


def get_file_content_as_base64(path, urlencoded=False):
    """
    获取文件base64编码
    :param path: 文件路径
    :param urlencoded: 是否对结果进行urlencoded
    :return: base64编码信息
    """
    with open(path, "rb") as f:
        content = base64.b64encode(f.read()).decode("utf8")
        if urlencoded:
            content = urllib.parse.quote_plus(content)
    return content


def get_access_token():
    """
    使用 AK，SK 生成鉴权签名（Access Token）
    :return: access_token，或是None(如果错误)
    """
    url = "https://aip.baidubce.com/oauth/2.0/token"
    params = {"grant_type": "client_credentials", "client_id": API_KEY, "client_secret": SECRET_KEY}
    return str(requests.post(url, params=params).json().get("access_token"))


if __name__ == '__main__':
    main()